import { Graph } from '@antv/x6'
import { register as registerVueNode } from '@antv/x6-vue-shape'
import { Selection } from '@antv/x6-plugin-selection'
import { debounce } from '@/utils'
import ToolDraw from './tool.draw'
export function registerNode(name, data) {
	if (data.component) {
		registerVueNode({
			shape: name,
			...data
		})
		return
	}

	Graph.registerNode(name, data, true)
}

export function registerEdge(name, data) {
	Graph.registerEdge(name, data, true)
}

class WorkFlow {
	inited
	graphEle

	edgeIndex = 10000
	// 配置项
	option
	// x6 Graph 的实例
	graph

	// layout 的节流函数
	debounceLayout

	// nodeMap = new Map()
	edgeMap = new Map()

	// rootNodes = []

	initNodeList = []
	initEdgeList = []

	nodeTree = []

	// 节点串联后的通道路线 eq. [nodeid, nodeid, nodeid]
	channelRoutes = []

	toolDraw = new ToolDraw()

	constructor(option = {}) {
		this.option = {
			el: document.querySelector('#workflow'),
			dir: 'TB',
			startID: '0',
			endID: '-1',
			nodeShape: 'rect',
			edgeShape: 'edge',
			edgeEndShape: 'edge-end',
			edgeAttrs: {
				line: {
					stroke: '#A2B1C3',
					strokeWidth: 2
				}
			},
			edgeValidAttrs: {
				shape: 'edge',
				labels: [
					{
						attrs: {
							label: {
								text: '已通过'
							}
						}
					}
				]
			},
			edgeInvalidAttrs: {
				shape: 'edge',
				labels: [
					{
						attrs: {
							label: {
								text: '未通过'
							}
						}
					}
				]
			},
			useSelection: false,
			// useSnapline: false,
			// useClipboard: false,
			// useKeyboard: false,
			// useResize: false,
			// useMagnet: false,
			// useClone: false,
			// useHistory: false,
			// useContextMenu: false,
			// useToolbar: false,
			// useMinimap: false,
			// useLoading: false,
			...option
		}

		this.graph = undefined
		this.inited = false
		this.graphEle = null
		this.debounceLayout = null

		this.toolDraw.setEnd(this.endID)
	}

	// 起始节点预制 ID
	get startID() {
		return this.option.startID ?? '0'
	}

	// 终点节点预制 ID
	get endID() {
		return this.option.endID ?? '-1'
	}
	// 默认的 node shape
	get nodeShape() {
		return this.option.nodeShape ?? 'rect'
	}

	// 默认的 edge shape
	get edgeShape() {
		return this.option.edgeShape ?? 'edge'
	}

	// 为结束点单独配置的连接线 shape
	get edgeEndShape() {
		return this.option.edgeEndShape ?? 'edge-end'
	}

	// 链接线的基础属性
	get edgeAttrs() {
		return this.option.edgeAttrs ?? {
			line: {
				stroke: '#A2B1C3',
				strokeWidth: 2
			}
		}
	}

	// 链接线的基础属性
	get edgeValidAttrs() {
		return this.option.edgeValidAttrs ?? {
			labels: [
				{
					attrs: {
						label: {
							text: '已通过'
						}
					}
				}
			]
		}
	}

	// 链接线的基础属性
	get edgeInvalidAttrs() {
		return this.option.edgeInvalidAttrs ?? {
			labels: [
				{
					attrs: {
						label: {
							text: '未通过'
						}
					}
				}
			]
		}
	}

	/**
	 * 初始化实例
	 * @param option 配置项
	 */
	init(option = {}) {
		this.option = {
			...this.option,
			...option
		}

		this.graphEle = option.el
		this.toolDraw.setEnd(this.endID)

		if (!this.graphEle) {
			throw new Error('graphEle is null')
		}

		this.graph = new Graph({
			container: this.graphEle,
			// 是否监听容器大小改变，并自动更新画布大小
			autoResize: true,
			/**
			 * 定制节点和边的交互行为
			 * edgeMovable 连接线是否可移动
			 * edgeLabelMovable 连接线中的 label 是否可移动
			 * arrowheadMovable 箭头是否可移动
			 * vertexMovable 顶点是否可移动
			 * vertexAddable 是否可添加顶点
			 * vertexDeletable 是否可删除顶点
			 * useEdgeTools 是否使用链接线的工具栏
			 * nodeMovable 节点是否可移动
			 * magnetConnectable 是否开启磁贴效果
			 * stopDelegateOnDragging 拖动时是否停止代理
			 * toolsAddable 全局是否可添加工具栏
			 */
			interacting: {
				edgeMovable: false,
				nodeMovable: false
			},
			// 画布的最小最大缩放级别
			scaling: {
				min: 0.7, // 默认值为 0.01
				max: 2, // 默认值为 16
			},
			// 网格，默认使用 10px 的网格，但不绘制网格背景
			grid: true,
			// 画布是否可以拖拽平移，默认禁用
			panning: {
				enabled: true,
				eventTypes: ['leftMouseDown']
			},
			// 鼠标滚轮缩放，默认禁用
			mousewheel: {
				enabled: true,
				zoomAtMousePosition: true,
				factor: 1.2
			},
			/**
			 * 连线选项
			 * normal  	  默认路由，原样返回路径点。
			 * orth  	    正交路由，由水平或垂直的正交线段组成。
			 * oneSide  	受限正交路由，由受限的三段水平或垂直的正交线段组成。
			 * manhattan  智能正交路由，由水平或垂直的正交线段组成，并自动避开路径上的其他节点（障碍）。
			 * metro  	  智能地铁线路由，由水平或垂直的正交线段和斜角线段组成，类似地铁轨道图，并自动避开路径上的其他节点（障碍）。
			 * er  	      实体关系路由，由 Z 字形的斜角线段组成。
			 */
			connecting: {
				router: 'metro'
			}
		})

		this.inited = true

		this.debounceLayout = debounce(() => {
			this.rawLayout()
		}, 300, true)

		if (this.option.useSelection) {
			this.graph.use(
				new Selection({
					rubberband: true,
					showNodeSelectionBox: true,
				})
			)
		}

		// this.graph.on('append:contextmenu', ({node, e}: any) => {
		//   e.stopPropagation()
		//   console.log('🚀 ~ WorkFlow ~ this.graph.on ~ node:', node.getSize())
		// })

		this.graph.on('render:done', () => {
			this.layout()
		})
	}

	/**
	 * 初始化节点数据并且生成附加数据
	 * @param data 节点数据
	 */
	initNormalData(data) {
		// console.log('🚀 ~ WorkFlow ~ initNormalData ~ data:', data)
		// this.nodeMap.clear()
		this.edgeMap.clear()
		// this.rootNodes = []
		this.initEdgeList = []

		data.forEach((node) => {
			if (node.id === this.startID || node.id === this.endID || node.shape.startsWith('edge')) {
				if (node.source?.cell !== void 0 && node.target?.cell !== void 0) {
					// 添加到初始化的 edge 中
					this.initEdgeList.push(node)
					// 内置的 edge id 需要做更新
					this.edgeIndex++
					this.edgeMap.set(`${node.source.cell} ${node.target.cell}`, node.id)
				}
			} else {
				this.initNodeList.push(node)
			}
		})
	}

	/**
	 * 格式化数据，用于计算节点位置
	 */
	formatAssignData() {
		// 布局方向
		const dir = this.option.dir
		this.toolDraw.clear()
		const nodes = this.graph?.getNodes()
		const edges = this.graph?.getEdges()

		nodes?.forEach((node) => {
			this.toolDraw.setNode(node.id, node.size())
		})

		edges?.forEach((edge) => {
			const source = edge.getSource()
			const target = edge.getTarget()

			this.toolDraw.setEdge(source.cell, target.cell)
		})
	}

	/**
	 * 渲染节点数据
	 * @param data 节点数据
	 */
	render(data = []) {
		if (!data.length || !this.graph || !this.inited) {
			return
		}

		this.initNormalData(data)

		// this.graph.fromJSON({
		// 	nodes: this.initNodeList,
		// 	edges: this.initEdgeList
		// })
		this.graph.fromJSON({
			cells: data
		})

		this.formatAssignData()

		this.layout()
	}

	/**
	 * 清空画布
	 */
	clear() {
		this.edgeMap.clear()
		this.getCls(graph => graph.clearCells())
	}

	/**
	 * 重置画布
	 */
	reset() {
		this.clear()
		this.appendNode({
			id: this.startID,
			shape: 'node-start'
		})
		this.layout()
	}

	/**
	 * 导出图中的节点和链接线的数据
	 */
	toJson() {
		return this.getCls(graph => graph.toJSON())
	}

	/**
	 * 节点添加连接线并且自动布局
	 */
	layout() {
		this.debounceLayout?.()
	}

	rawLayout() {
		this.formatAssignData()
		this.toolDraw.layout()
		this.toolDraw.getNodes().forEach((id) => {
			const node = this.getNode(id)

			if (node) {
				const pos = this.toolDraw.getNode(id)

				node.position(pos.x, pos.y)
			}
		})

		this.graph?.centerContent()
	}

	// zoom(): number
	// zoom(scale: number): void
	/**
	 * 控制缩放的函数
	 * 
	 * 此函数有两种用法：
	 * 1. 无参数调用，返回当前的缩放级别
	 * 2. 带一个参数调用，设置新的缩放级别
	 * @returns 如果没有提供参数，返回当前缩放级别；否则返回void
	 */
	zoom(scale) {
		if (scale === undefined) {
			return this.graph?.zoom()
		}

		this.graph?.zoomTo(scale)
	}

	zoomToFit() {
		this.graph?.zoomToFit()
	}

	/**
	 * 销毁 x6 实例
	 */
	destroy() {
		this.graph?.dispose()
		this.graph = undefined
		this.inited = false
		this.graphEle = null
		this.debounceLayout?.cancel?.()
		this.debounceLayout = null
	}

	/**
	 * 绑定事件
	 * @param eventName 事件名称
	 * @param callback 事件回调
	 */
	on(eventName, callback) {
		this.graph?.on(eventName, callback)
	}

	getCls(callback) {
		if (!this.graph) {
			throw new Error('Graph is not initialized')
		}

		return callback(this.graph)
	}

	/**
	 * 获取节点
	 * @param id 节点 ID
	 * @returns 节点
	 */
	getNode(id) {
		return this.getCls((graph) => graph.getCellById(id))
	}

	/**
	 * 获取节点列表
	 * @param ids 节点 ID 集合
	 * @returns 节点
	 */
	getNodes(ids) {
		return this.getCls((graph) => {
			if (!ids.length) {
				return graph.getNodes()
			}

			const idMaps = ids.reduce((map, id) => {
				map[id] = 1
				return map
			}, {})

			return graph.getNodes().filter(node => idMaps[node.id])
		})
	}

	/**
	 * 返回所有叶子节点，即没有输出边的节点
	 * @returns Node[]
	 */
	getFinalNodes(){
		return this.getCls((graph) => graph.getLeafNodes())
	}

	/**
	 * 返回没有关联到终点的节点 集合
	 * @returns Node[]
	 */
	getLooseNodes() {
		return this.getFinalNodes().filter(node => node.id !== this.endID)
	}

	/**
	 * 返回没有关联到终点的节点 ID 集合
	 * @returns TNodeID[]
	 */
	getLooseNodeIds(){
		return this.getFinalNodes().reduce((list, node) => {
			if (node.id !== this.endID) {
				list.push(node.id)
			}

			return list
		}, [])
	}

	/**
	 * 获取节点的附属数据
	 * @param id 节点 ID
	 * @returns id 节点 id
	 * @returns node x6 节点实例
	 * @returns data x6 节点的附带数据，基于 node.getData 获取
	 * @returns from 当前节点为终点的节点 id 集合
	 * @returns to 当前节点为起点的节点 id 集合
	 */
	getExtraNode(id) {
		const node = this.getNode(id)

		return {
			id,
			node,
			data: node?.getData() || {},
			from: this.getFromIds(id),
			to: this.getToIds(id)
		}
	}

	// 临时函数。外部不能调用
	createNode(data) {
		return this.getCls((graph) => graph.createNode(data))
	}

	/**
	 * 是否存在节点/链接线
	 * @param id 节点 ID
	 * @returns 是否存在
	 */
	hasNode(id) {
		return this.getCls((graph) => graph.hasCell(id))
	}

	/**
	 * 创建并添加节点
	 * @param data 
	 * @param option 
	 * @returns 
	 */
	appendNode(data = {}, option = {}) {
		return this.getCls((graph) => {
			const node = graph.addNode({ shape: this.nodeShape, ...data }, option)

			return node
		})
	}

	/**
	 * 创建并添加多个节点
	 * @param data 
	 * @param option 
	 * @returns 
	 */
	appendNodes(data, option = {}) {
		return this.getCls((graph) => data.map((item) => graph.addNode({ shape: this.nodeShape, ...item }, option)))
	}

	/**
	 * 添加并关联新的节点
	 * @param node 需要新建的节点数据
	 * @param from 需要关联的起始节点 id 或集合
	 * @param to  需要关联的终点节点 id 或集合
	 */
	appendAssignNode({
		node,
		option = {},
		edge = {},
		from = [],
		to = []
	}) {
		const nodes = this.appendNodes(Array.isArray(node) ? node : [node], option);

		nodes.forEach((item) => {
			this.assignNodes(from, item.id);
			this.assignNodes(item.id, to);
		});
	}

	/**
	 * 删除节点，并且同步删除链接线和相关附属数据
	 * @param id 节点 ID
	 * @param keepAssign 删除后是否把之前的下级节点关联到之前的链接节点
	 */
	removeNode(id, keepAssign) {
		const fromEdges = this.getFromEdges(id)
		const toEdges = this.getToEdges(id)

		// 删除终点为当前节点的链接线
		fromEdges.forEach((edge) => {
			this.removeEdge(edge.id, id)

			if (keepAssign) {
				toEdges.forEach((to) => {
					if (edge.getSourceCellId() == this.startID && to.getTargetCellId() == this.endID) {
						return
					}

					this.assignNode(edge.getSourceCellId(), to.getTargetCellId())
				})
			}
		})

		// 删除起点为当前节点的链接线
		toEdges.forEach((edge) => {
			this.removeEdge(id, edge.id)
		})

		return this.getCls((graph) => graph.removeNode(id))
	}

	/**
	 * 删除多个节点，并且同步删除链接线和相关附属数据
	 * @param ids 节点 ID
	 * @param keepAssign 删除后是否把之前的下级节点关联到之前的链接节点
	 */
	removeNodes(ids, keepAssign) {
		let flag = true
		ids.forEach(id => {
			if (!this.removeNode(id, keepAssign)) {
				flag = false
			}
		})

		return flag
	}

	/**
	 * 删除终点节点
	 * @param validFrom 还有关联到终点的节点时，是否删除
	 */
	removeEnd(validFrom) {
		const fromIds = this.getFromIds(this.endID)

		if (validFrom && fromIds.length) {
			return
		}

		this.removeNode(this.endID)
	}

	/**
	 * 删除节点后后续相关联的所有节点
	 * @param id 节点 ID
	 * @param endID 删除路径的终点节点 ID，目前只支持单终点判断
	 * @param keepAssign 删除后是否把之前的下级节点关联到之前的链接节点
	 */
	removePath(id, endID, keepAssign) {
		this.getNodePath(id, endID).forEach(nodeID => {
			this.removeNode(nodeID, keepAssign)
		})
	}

	/**
	 * 获取当前节点为终点的节点 ID
	 * @param id 节点 ID
	 * @returns TNodeID[]
	 */
	getFromIds(id) {
		return this.getFromEdges(id).map((edge) => edge.getSourceCellId())
	}

	/**
	 * 获取当前节点为起点的节点 ID
	 * @param id 节点 ID
	 * @returns TNodeID[]
	 */
	getToIds(id){
		return this.getToEdges(id).map((edge) => edge.getTargetCellId())
	}

	/**
	 * 获取当前节点为终点的链接线
	 * @param id 节点 ID
	 * @returns Edge[]
	 */
	getFromEdges(id) {
		return this.getCls((graph) => graph.getIncomingEdges(id) || [])
	}

	/**
	 * 获取当前节点为起点的链接线
	 * @param id 节点 ID
	 * @param onlyID 是否只返回 id
	 * @returns Edge[]
	 */
	getToEdges(id, onlyID) {
		return this.getCls((graph) => graph.getOutgoingEdges(id) || [])
	}

	/**
	 * 获取连接线
	 * @param source 起点 node id 或者直接就是 edge node id
	 * @param target 终点 node id
	 * @returns 
	 */
	getEdge(source, target) {
		let edgeID = source

		if (source !== undefined && target !== undefined) {
			edgeID = this.edgeMap.get(`${source} ${target}`)
		}

		return this.getCls((graph) => graph.getCellById(edgeID))
	}

	/**
	 * 获取链接线列表
	 * @param ids 链接线 ID 集合
	 * @returns 链接线
	 */
	getEdges(ids){
		return this.getCls((graph) => {
			if (!ids.length) {
				return graph.getEdges()
			}

			const idMaps = ids.reduce((map, id) => {
				map[id] = 1
				return map
			}, {})

			return graph.getEdges().filter(node => idMaps[node.id])
		})
	}

	/**
	 * 创建链接线
	 * @param data 创建数据
	 * @returns 已经创建的 edge 节点
	 */
	createEdge(data) {
		return this.getCls((graph) => graph.createEdge({
			shape: this.edgeShape,
			attrs: this.edgeAttrs,
			...data
		}))
	}

	/**
	 * 在画布中添加一个连接线
	 * @param data.source 起点 node id
	 * @param data.target 终点 node id
	 * @returns 
	 */
	appendEdge(data) {
		return this.getCls((graph) => {
			const id = this.edgeMap.get(`${data.source} ${data.target}`)

			if (id !== undefined) {
				return this.getEdge(id)
			}

			const edge = graph.addEdge({
				id: this.edgeIndex,
				shape: data.target == this.endID ? this.edgeEndShape : this.edgeShape,
				...data,
				attrs: { ...this.edgeAttrs, ...data.attrs },
			})

			this.edgeMap.set(`${data.source} ${data.target}`, edge.id)
			this.edgeIndex++

			return edge
		})
	}

	/**
	 * 删除连接线
	 * @param source 起点 node id 或者直接就是 edge node id
	 * @param target 终点 node id
	 */
	removeEdge(source, target) {
		const edge = this.getEdge(source, target)

		if (!edge) {
			return
		}

		source = edge.getSourceCellId?.()
		target = edge.getTargetCellId?.()
		if (source === void 0 || target === void 0) {
			return
		}
		this.edgeMap.delete(`${source} ${target}`)

		edge.remove()
	}

	/**
	 * 删除多个连接线
	 * @param source 起点 node id
	 * @param target 终点 node id
	 */
	removeEdges(source, target) {
		source = Array.isArray(source) ? source : [source]
		target = Array.isArray(target) ? target : [target]

		source.forEach((sourceId) => {
			target.forEach((targetId) => {
				this.removeEdge(sourceId, targetId)
			})
		})
	}

	/**
	 * 关联两个节点
	 * @param source 起点 node id
	 * @param target 终点 node id
	 * @param edge 连接线的扩展属性
	 */
	assignNode(source, target, edge = {}) {
		if (this.edgeMap.get(`${source} ${target}`)) {
			return
		}

		if (!this.hasNode(source) || !this.hasNode(target)) {
			return
		}

		this.appendEdge({
			source: source,
			target: target,
			...edge
		})
	}

	/**
	 * 关联多个节点
	 * @param source 起点 node id
	 * @param target 终点 node id
	 * @param edge 连接线的扩展属性
	 */
	assignNodes(source = [], target = [], edge = {}) {
		source = Array.isArray(source) ? source : [source]
		target = Array.isArray(target) ? target : [target]

		source.forEach((sourceId) => {
			target.forEach((targetId) => {
				this.assignNode(sourceId, targetId, edge)
			})
		})
	}

	/**
	 * 获取节点路径
	 */
	getNodePath(id, end) {
		const node = this.getExtraNode(id)

		return [id, ...this.getNextPath(node.to, end)]
	}

	getNextPath(ids, end) {
		return ids.reduce((list, id) => {
			list.push(id)

			if (id != end) {
				list.push(...this.getNextPath(this.getToIds(id), end))
			}

			return list
		}, [])
	}
}

export default WorkFlow